<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Chat\Event\TalkMessageRevokeEvent;
use App\Chat\Event\TalkRecordEvent;
use App\Chat\Service\TalkMessageService;
use App\CustHub\Constants\ContactStatusConstants;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Constants\RoomOpTypeConstants;
use App\CustHub\Event\AutoKickTrigger;
use App\CustHub\Event\CtrlRoomTrigger;
use App\CustHub\Event\MaterialCollectTrigger;
use App\CustHub\Event\RobotInfoChange;
use App\CustHub\Event\RoomInviteAcceptTrigger;
use App\CustHub\Event\RoomBroadcastTrigger;
use App\CustHub\Event\SmartAnswerTrigger;
use App\CustHub\Mapper\ChChatRecordMapper;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoomMemberMapper;
use App\CustHub\Model\ChContact;
use App\CustHub\Model\ChMaterial;
use App\CustHub\Model\ChRobot;
use App\Robot\Service\RobotMsgService;
use Carbon\Carbon;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Di\Annotation\Inject;
use Psr\EventDispatcher\EventDispatcherInterface;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Collection\data_get;
use function Hyperf\Coroutine\co;
use function Hyperf\Support\optional;

class EasyWorkNotifyService extends AbstractNotifyService
{


    public function notify(array $data)
    {
        $uuid = $data['guid'];
        $now = Carbon::now()->toDateTimeString();
        logger()->info("{$now}机器人{$uuid}接收消息：" . json_encode($data, JSON_UNESCAPED_UNICODE));
        $data['user_id'] = (string)$data['user_id'];
        isset($data['sender_id']) && $data['sender'] = (string)$data['sender_id'];
        isset($data['receiver_id']) && $data['receiver'] = (string)$data['receiver_id'];
        isset($data['from_room_id']) && $data['from_room_id'] = (string)$data['from_room_id'];

        $robotInfo = $this->robotMapper->findByRobotKey($uuid);
        if(empty($robotInfo)){
            return;
        }

        $cmd = $data['cmd'];
        switch ($cmd){
            case 11000:
                if(!empty($robotInfo)){
                    $robotInfo->robot_key = $data['clientKey'];
                    $robotInfo->save();
                    $this->robotMapper->clearCache($robotInfo->id,$data['clientKey']);
                }
                //扫码登录通知 五分钟
                redis()->set("robot_login_result:".$data['login_qrcode_key'],$data,60 * 60);
                break;
//            case 11001:
//                //登录成功通知
//                container()->get(RobotLoginService::class)->loginSuccess($robotInfo,$uuid);
//                break;
//            case 11002:
//                if(isset($data['logout_type']) && $data['logout_type'] == 3){
//                    //心跳失败 自动登录
//                    $params = [
//                        'main_uid' => $robotInfo->main_uid,
//                        'id' => $robotInfo->id,
//                    ];
//                    try {
//                        container()->get(RobotLoginService::class)->autoLogin($params);
//                    }catch (\Throwable $throwable){
//                        stdout_log()->error('自动登录失败:'.$robotInfo->id.'-errmsg:'.$throwable->getMessage());
//                        container()->get(RobotLoginService::class)->loginOutNotify($robotInfo);
//                    }
//                }else{
//                    container()->get(RobotLoginService::class)->loginOutNotify($robotInfo);
//                }
//                //账号退出登录通知
//                break;
            case 11016:
                //账号状态变化消息
                $status = data_get($data,'msg_data.status');
                //重启服务器的情况
                $serverReboot = data_get($data,'msg_data.server_reboot');
                if($status == 2){
                    //在线
                    container()->get(RobotLoginService::class)->loginSuccess($robotInfo,$uuid);
                }elseif (!$serverReboot && ($status == 0 || $status == -1)){
                    //离线
                    logger()->info('账号状态变化消息:'.$robotInfo->id.'-status:'.$status);
                    if($status == -1){
                        logger()->info('账号状态变化消息:'.$robotInfo->id.'-status:'.$status.'退出登錄');
                        container()->get(RobotLoginService::class)->loginOutNotify($robotInfo);
                    }

                    if($status == 0){
                        try {
                            container()->get(RobotLoginService::class)->autoLogin([
                                'main_uid' => $robotInfo->main_uid,
                                'id' => $robotInfo->id,
                            ]);
                        }catch (\Throwable $throwable){
                            logger()->info('自动登录失败:'.$robotInfo->id.'-errmsg:'.$throwable->getMessage());
                        }
                    }
                }
                break;
            case 104012:
            case 15000:
                $msgType = $data['msg_type'];
                $roomOpTypes = [1001,1002,1003,1004,1005,1006,1022,1023,1043,2308];
                if(in_array($msgType,$roomOpTypes)){
                    $roomOpType = match($msgType){
                        1001,1022,1043,2308 => RoomOpTypeConstants::ROOM_INFO_CHANGE,//群信息变更 1043设置管理员
                        1002 => RoomOpTypeConstants::JOIN_ROOM,//加入群
                        1003,1004,1005 => RoomOpTypeConstants::OUT_ROOM,//退出群
                        1006 => RoomOpTypeConstants::CREATE_ROOM,//创建群
                        1023 => RoomOpTypeConstants::DISMISS_ROOM,//解散群
                    };
                    $data['room_op_type'] = $roomOpType;
                    $this->handleRoom($data,$robotInfo);
                }else{
                    //消息通知
                    $this->handleMessage($data,$robotInfo);
                }
                break;
            case 15001:
                //群相关
                $this->handleRoom($data,$robotInfo);
                break;
            case 15500:
                //【置顶/免打扰/保存群到通信录】消息通知
                $this->handleSystemNotification($data,$robotInfo);
                break;
            default:
                break;
        }
    }

    private function handleMessage(array $data,ChRobot $robotInfo)
    {

        $msgtype = data_get($data,'msg_type');
        $data['msgtype'] = $msgtype;

        $content = data_get($data,'msg_data.content','');
        $data['content'] = $content;
        $msgData = data_get($data,'msg_data',[]);
        $data['at_list'] = data_get($data,'msg_data.at_list',[]);

        //是否个微用户发送的消息
        $isRobot = $data['user_id'] == $data['sender'];
        $data['is_robot'] = $isRobot;

        $data['is_room'] = $data['from_room_id'] != 0 ? 1 : 0;

        $needSaveChatRecord = false;

        $msg_type = match($msgtype){
            0,2 => !empty($data['at_list']) ? MsgTypeConstants::MIXED_TEXT : MsgTypeConstants::TEXT,
            7,14,101 => MsgTypeConstants::IMAGE,
            22,23,103 => MsgTypeConstants::VIDEO,
            15,20,102 => MsgTypeConstants::FILE,
            29,104 => MsgTypeConstants::EMOJI,
            13 => MsgTypeConstants::URL,
            78 => MsgTypeConstants::MINI_PROGRAM,
            141 => MsgTypeConstants::VIDEO_CHANNEL,
            default => ''
        };

        if(!empty($msg_type)){
            if($msg_type == MsgTypeConstants::MIXED_TEXT){
                $content = $this->parseContent($content,$data['at_list']);
            }

            co(function () use ($robotInfo,$data,$msg_type,$content){
                $data['robot_id'] = $robotInfo->id;
                $data['content'] = [
                    'content' => $content,
                    'msg_type' => $msg_type,
                ];
                event(new TalkRecordEvent($robotInfo,$data));
            });
        }
        //统计群成员最后发言时间
        if($data['is_room']){
            container()->get(ChRoomMemberMapper::class)->findByRoomWxidAndUserId($data['from_room_id'],$data['sender']);
        }

        if($data['is_room'] && in_array($msgtype,[0,2])){
            $roomId = $data['from_room_id'];
            co(function () use ($robotInfo,$roomId){
                Coroutine::sleep(rand(5,10));
                container()->get(RoomReceiveCallBackService::class)->checkRoom($robotInfo,$roomId);
            });
        }

        switch ($msgtype){
            case 0:
            case 2:
                $needSaveChatRecord = true;
                if($data['is_room'] == 0){
                    if(!$this->checkAddFriendKey($data)){
                        container()->get(ContactReceiveCallBackService::class)->messageHandler($data,$robotInfo);
                    }
                    container()->get(ChRoomKeywordService::class)->getMeetTheConditionScheme([
                        'robot_user_id' => $data['user_id'],
                        'user_id' => $isRobot ? $data['receiver'] : $data['sender'],
                        'content' => $data['content'],
                        'is_robot' => $data['is_robot'],
                        'robotInfo' => $robotInfo->toArray(),
                    ]);
                    container()->get(ChKeywordTagSchemeService::class)->triggerScheme($data,$robotInfo);
                }else{
                    //群消息
                    container()->get(RoomReceiveCallBackService::class)->roomMessage($robotInfo,$data);
                    event(new CtrlRoomTrigger($robotInfo,$data,1));
                }
                break;
            case 4:
                //聊天合集
                $needSaveChatRecord = true;
                break;
            case 14:// 图片
            case 7:
            case 101:
                //我是一条图片消息
                $needSaveChatRecord = true;
                $data['msg_data']['file_name'] = $data['filename'] ?? '';
                $content = container()->get(RobotMsgService::class)->processMessages($robotInfo,$msg_type,$data);
                if($data['is_room']){
                    $data['content'] = $content;
                    event(new CtrlRoomTrigger($robotInfo,$data,2));
                }
                break;
            case 23:// 视频
            case 22:
            case 103:
                //我是一条视频消息
                $needSaveChatRecord = true;
                $data['msg_data']['file_name'] = $data['filename'] ?? '';
                $content = container()->get(RobotMsgService::class)->processMessages($robotInfo,$msg_type,$data);
                break;
            case 15:// 文件
            case 20:
            case 102:
                //我是一条文件消息
                $data['msg_data']['file_name'] = $msgData['filename'] ?? '';
                $data['msg_data']['file_name_ext'] = $msgData['filename_ext'] ?? '';
                $content = container()->get(RobotMsgService::class)->processMessages($robotInfo,$msg_type,$data);
                $needSaveChatRecord = true;
                break;
            case 29:// GIF
            case 104:
                //我是一条GIF消息
                $content = container()->get(RobotMsgService::class)->processMessages($robotInfo,$msg_type,$data);
                $needSaveChatRecord = true;
                break;
//            case 20:// 大文件消息 >20M
//                //我是一条大文件消息
//                $needSaveChatRecord = true;
//                $msgData['file_name'] = base64_decode($msgData['file_name']);
//                break;
//            case 22:// 大视频 >20M
//                //我是大视频消息
//                $needSaveChatRecord = true;
//                break;
            case 6:// 位置
                //我是一条位置消息
                $needSaveChatRecord = true;
                $msgData['title'] = decodeIfBase64($msgData['title']);
                $msgData['address'] = decodeIfBase64($msgData['address']);
                break;
            case 13:// 链接
                $content = container()->get(RobotMsgService::class)->processMessages($robotInfo,$msg_type,$data);
                !empty($content) && $msgData['cover']=$content;
                $msgData['url']=$msgData['link_url'];
                //我是一条链接消息
                $needSaveChatRecord = true;
                break;
            case 41:// 名片
                $needSaveChatRecord = true;
                break;
            case 26:// 红包
                break;
            case 16:// 语音
                break;
            case 78:// 小程序
                $needSaveChatRecord = true;
//                $isPersonal = $data['user_id'] != $data['sender'] ? 1 : 0;
                $isPersonal = 0;
                $msgData['cover_image_aeskey'] = $msgData['cover_image_aes_key'];
                //$this->saveMiniProgram($robotInfo,$data['msg_data'],$isPersonal);
                break;
            case 123:// 图文混合
                $needSaveChatRecord = true;
                break;
            case 146:// 直播
                break;
            case 141:
                $needSaveChatRecord = true;
                //视频号
                //插入素材库
//                $isPersonal = $data['user_id'] != $data['sender'] ? 1 : 0;
                $isPersonal = 0;
                //$msgData['channel_name'] = base64_decode($msgData['channel_name']);
                //$this->saveVideoChannel($robotInfo,$data['msg_data'],$isPersonal);
                break;
            case 213:
                //群接龙
                $needSaveChatRecord = true;
                break;
            case 2001:// 消息已读通知
                break;
            case 2005:// 消息未读
                break;
            case 1011://需要添加好友
                if(isset($data['msg_data']['base64_raw_data'])){
                    //添加好友处理
                    redis()->set($this->getAddFriendKey($data),1,30);
                    $data['content'] = base64_decode($data['msg_data']['base64_raw_data']);
                    if(str_contains($data['content'],'对方拒收') ){
                        container()->get(ChContactMapper::class)->updateByCondition([
                            'robot_id'=>$robotInfo['id'],
                            'user_id'=>$data['receiver'],
                            'status'=>ContactStatusConstants::NORMAL_FRIEND
                        ],['status'=>ContactStatusConstants::BLOCKED_FRIEND,'friend_loss_time'=>datetime(time())]);
                    }
                    container()->get(ContactReceiveCallBackService::class)->messageHandler($data,$robotInfo);
                    $msg_unique_identifier = $data['msg_unique_identifier'] ?? '';
                    if(str_contains($msg_unique_identifier,'_need_verify')){
                        co(function () use ($robotInfo,$data){
                            container()->get(TalkMessageService::class)->recordHandle($robotInfo,$data);
                        });
                    }
                }
                break;
            case 1029:
                //群邀请确认通知
                $msgData = $data['msg_data'];
                $msgData['invite_msg_server_id'] = $data['msg_server_id'];
                event(new RoomInviteAcceptTrigger($robotInfo,$data['from_room_id'],$msgData));
                break;
            case 1052:
                //设置置顶
                break;
            case 1055:
                //取消置顶
                break;
            case 2118:
                //群信息变更通知
                if($data['is_room'] == 1 && !$this->isRoomNotificationProcessed($data['from_room_id'])){
                    $data['room_op_type'] = RoomOpTypeConstants::ROOM_INFO_CHANGE;
                    $this->handleRoom($data,$robotInfo);
//                    container()->get(RoomReceiveCallBackService::class)->syncRoomHandler($data,$robotInfo);
                }
                break;
            case 2063:
                //撤回消息
                if($data['is_room']){
                    event(new AutoKickTrigger($robotInfo,$data,1));
                }

                $revoke_msg_unique_identifier = data_get($msgData,'revoke_msg_unique_identifier');
                stdout_log()->info('撤回消息：'.$revoke_msg_unique_identifier);
                if(!empty($revoke_msg_unique_identifier)){
                    event(new TalkMessageRevokeEvent($robotInfo->id,$revoke_msg_unique_identifier));
                }
                break;
        }

        if($needSaveChatRecord){
            $data['robot_id'] = $robotInfo->id;
            $data['msg_data'] = $msgData;
            $data['content'] = [
                'content' => $content,
                'msg_type'=> $msg_type
            ];
            $data['send_time'] = time();
            $chatRecordMapper = container()->get(ChChatRecordMapper::class);
            $chatRecordId = $chatRecordMapper->save($data);

            if($data['sender'] != $data['user_id'] && $data['is_room'] == 0){
                //记录客户最后一次发言时间
                ChContact::query()
                    ->where('robot_id',$robotInfo->id)
                    ->where('user_id',$data['sender'])
                    ->update([
                        'last_send_msg_time' => Carbon::now()->toDateTimeString()
                    ]);
            }

            if($data['is_room']){
                event(new AutoKickTrigger($robotInfo,$data));
                //群转播
                event(new RoomBroadcastTrigger($robotInfo,$data));
            }

            if($msg_type == MsgTypeConstants::TEXT || $msg_type == MsgTypeConstants::IMAGE || $msg_type == MsgTypeConstants::MIXED_TEXT){
                //触发智能应答
                co(function ()use($data,$msg_type){
                    $this->eventDispatcher->dispatch(new SmartAnswerTrigger($data));
                });
            }
            if(in_array($msg_type,MsgTypeConstants::collectMsgTypes())){
                co(function ()use($data,$robotInfo){
                    //logger()->info('素材库数据测试：'.json_encode($data));
                    $this->eventDispatcher->dispatch(new MaterialCollectTrigger($robotInfo,$data));
                });
            }
            if(!$isRobot){
                $data['chat_record_id'] = $chatRecordId;
                event(new CtrlRoomTrigger($robotInfo,$data));
            }
        }

    }

    /**
     * 群相关
     * @param array $data
     * @param ChRobot $robotInfo
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function handleRoom(array $data,ChRobot $robotInfo): void
    {
        $cacheKey='room_notice_handle_set:'.$data['msg_unique_identifier'];
        if(redis()->exists($cacheKey)){
            return;
        }
        if(isset($data['room_op_type'])){
            $lock = lock('room_notice_handle_lock'.$data['from_room_id'].'_'.$data['room_op_type'],  4);
            try {
                $lock->block(3);
                $this->markRoomNotificationAsProcessed($data['from_room_id']);
                redis()->set($cacheKey,1,30);
                container()->get(RoomReceiveCallBackService::class)->roomOp($data,$robotInfo);
            } catch (LockTimeoutException $e) {
                // 无法获取锁...
            } finally {
                optional($lock)->release();
            }
        }
    }
}